package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private TrackStatMapper trackStatMapper;

	@Autowired
	private VodService vodService;

	@Autowired
	private UserFeignClient userFeignClient;

	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 保存声音
	 * @param trackInfoVo 声音信息vo对象
	 * @param userId 用户ID
	 */
	@Override
	public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
		// 1.新增声音记录
		 // 1.1 将声音VO转为PO
		TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
		// 1.2 为声音对象传参：用户ID、声音在专辑中的排序值、声音来源、声音状态
		trackInfo.setUserId(userId);
		 // 1.2.1 序号：根据专辑ID查询专辑信息得到声音数量，用于计算新增声音序号
		AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
		Assert.notNull(albumInfo,"专辑不存在");
		trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
		trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
		trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
		// 1.3 远程调用腾讯云点播平台根据文件唯一标识获取音频文件详情:声音时长、音频大小、音频类型
		TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
		trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
		trackInfo.setMediaSize(trackMediaInfoVo.getSize());
		trackInfo.setMediaType(trackMediaInfoVo.getType());
		// 1.4 新增声音记录得到的声音主键id
		trackInfoMapper.insert(trackInfo);
		Long trackInfoId = trackInfo.getId();

		// 2.更新声音所属专辑的声音数量
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
		albumInfoMapper.updateById(albumInfo);

		// 3. 新增声音统计表记录
		this.saveTrackStat(trackInfoId,SystemConstant.TRACK_STAT_PLAY,0);
		this.saveTrackStat(trackInfoId,SystemConstant.TRACK_STAT_COLLECT,0);
		this.saveTrackStat(trackInfoId,SystemConstant.TRACK_STAT_PRAISE,0);
		this.saveTrackStat(trackInfoId,SystemConstant.TRACK_STAT_COMMENT,0);
	}

	/**
	 * 保存声音统计信息
	 *
	 * @param trackId  声音ID
	 * @param statType 统计类型 0701-播放量 0702-收藏量 0703-点赞量 0704-评论数
	 * @param num      数值
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveTrackStat(Long trackId, String statType, int num) {
		TrackStat trackStat = new TrackStat();
		trackStat.setTrackId(trackId);
		trackStat.setStatType(statType);
		trackStat.setStatNum(num);
		trackStatMapper.insert(trackStat);
	}

	/**
	 * TODO:该接口需要登录才能访问
	 * 获取当前用户声音分页列表
	 * @param pageInfo MP分页对象
	 * @param trackInfoQuery 查询声音条件对象
	 * @return
	 */
	@Override
	public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
		return trackInfoMapper.getUserTrackPage(pageInfo,trackInfoQuery);
	}

	/**
	 * 修改声音信息
	 * @param id
	 * @param trackInfoVo
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
		// 1.根据声音ID查询声音信息
		TrackInfo trackInfo = trackInfoMapper.selectById(id);
		Assert.notNull(trackInfo,"声音不存在");
		// 2.判断音频信息是否发生变化
		String mediaFiledBefore = trackInfo.getMediaFileId();
		String mediaFiledAfter = trackInfoVo.getMediaFileId();
		// 将用户提交的声音拷贝到PO对象中
		BeanUtil.copyProperties(trackInfoVo,trackInfo);
		if (!mediaFiledBefore.equals(mediaFiledAfter)){
			// 3.调用云点播平台获取最新音频文件信息 更新 声音中音频信息
			TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(mediaFiledAfter);
			trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfo.getDuration()));
			trackInfo.setMediaType(mediaInfo.getType());
			trackInfo.setMediaSize(mediaInfo.getSize());
			// 4.调用腾讯云点播平台删除旧音频
			vodService.deleteMedia(trackInfo.getMediaFileId());
		}
		// 5.更新声音信息表
		trackInfoMapper.updateById(trackInfo);
	}

	/**
	 * 删除声音信息
	 * @param id
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void removeTrackInfo(Long id) {
		// 1.获取将要被删除声音对象,得到序列号
		TrackInfo trackInfo = trackInfoMapper.selectById(id);
		Assert.notNull(trackInfo,"声音不存在");
		Integer orderNum = trackInfo.getOrderNum();
		// 2.修改序列号比当前声音大的序列号 -1操作
		trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(),orderNum);

		// 3.执行删除声音
		trackInfoMapper.deleteById(trackInfo);

		// 4.更新专辑包含声音数量-1
		Long albumId = trackInfo.getAlbumId();
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() -1);
		albumInfoMapper.updateById(albumInfo);

		// 5.删除云点播文件
		String mediaFileId = trackInfo.getMediaFileId();
		vodService.deleteMedia(mediaFileId);

	}

	/**
	 * 分页获取当前用户查看专辑的声音列表（动态判断付费标识）
	 * @param userId
	 * @param albumId
	 * @param pageInfo
	 * @return
	 */
	@Override
	public Page<AlbumTrackListVo> getAlbumTrackPage(Long userId, Long albumId, Page<AlbumTrackListVo> pageInfo) {
		// 1. TODO 分页专辑ID查询声音列表-默认专辑付费标识为flase
		pageInfo = trackInfoMapper.getAlbumTrackPage(pageInfo,albumId);
		// 2. 根据专辑ID查询专辑信息-得到专辑付费类型
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		Assert.notNull(albumInfo,"专辑{}不存在！",albumId);
		String payType = albumInfo.getPayType();
		// 3. 处理未登录的情况
		if (userId == null){
			// 3.1 判断专辑付费类型是否为：VIP免费（0102）、付费（0103）
			if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)){
				// 3.2 遍历声音列表-将免费试听以外的声音付费标识设置为true
				pageInfo.getRecords().stream().filter(albumTrackListVo -> {
					return albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree();
				}).forEach(albumTrackListVo -> {
					albumTrackListVo.setIsShowPaidMark(true);
				});
			}
		}else {
			// 4. 处理已登录情况
			Boolean isNeedCheckPayStatus = false;
			// 4.1 远程调用用户服务获取用户信息得到用户身份
			UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
			Assert.notNull(userInfoVo,"当前用户：{}为空",userId);
			// 4.2 处理付费类型为VIP免费且当前用户为普通用户/VIP过期
			Integer isVip = userInfoVo.getIsVip();
			if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)){
				// 普通用户
				if (isVip.intValue() == 0){
					isNeedCheckPayStatus = true;
				}

				// VIP过期
				if (isVip.intValue() == 1 && new Date().after(userInfoVo.getVipExpireTime())){
					isNeedCheckPayStatus = true;
				}
			}

			// 4.3 处理付费类型为 付费
			if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)){
				isNeedCheckPayStatus = true;
			}
			// 4.4 统一对需要进一步确认购买情况进行处理
			if (isNeedCheckPayStatus){
				List<AlbumTrackListVo> trackListVoList = pageInfo.getRecords();
				// 4.4.1 远程调用用户信息-查询本页中声音购买情况（将免费试听去掉）
				 trackListVoList = trackListVoList.stream()
						// 将免费试听集排除
						.filter(albumTrackListVo ->
								albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()
						).collect(Collectors.toList());

				// 将声音VO集合转为声音id集合
				List<Long> needCheckTrackIdList = trackListVoList
						.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());

				Map<Long, Integer> payStatusMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckTrackIdList).getData();

				// 4.4.2 遍历本页声音（将免费试听去掉），判断未购买付费标识设置为true
				for (AlbumTrackListVo albumTrackListVo : trackListVoList) {
					if (payStatusMap.get(albumTrackListVo.getTrackId()) == 0){
						albumTrackListVo.setIsShowPaidMark(true);
					}
				}
			}
		}
		return pageInfo;
	}

	/**
	 * 更新声音及所属的专辑的统计信息
	 * @param trackStatMqVo
	 */
	@Override
	public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
		// 1. 确保消息幂等性（避免同一消息被MQ多次投递）
		String businessNo = trackStatMqVo.getBusinessNo();
		Boolean flag = redisTemplate.opsForValue().setIfAbsent(businessNo, "", 1, TimeUnit.MINUTES);
		if (flag){
			// 说明第一次处理该业务消息
			// 2.更新声音统计信息
			trackInfoMapper.updateStat(trackStatMqVo.getTrackId(),trackStatMqVo.getStatType(),trackStatMqVo.getCount());
			// 3.更新专辑统计信息 专辑跟声音共有统计类型：播放、评论 当声音统计被更新，所属的专辑才更新
			if (SystemConstant.TRACK_STAT_PLAY.equals(trackStatMqVo.getStatType())){
				albumInfoMapper.updateStat(trackStatMqVo.getAlbumId(),SystemConstant.ALBUM_STAT_PLAY,trackStatMqVo.getCount());
			}
			if (SystemConstant.TRACK_STAT_COMMENT.equals(trackStatMqVo.getStatType())){
				albumInfoMapper.updateStat(trackStatMqVo.getAlbumId(),SystemConstant.ALBUM_STAT_COMMENT,trackStatMqVo.getCount());
			}
		}
	}

	/**
	 * 根据声音ID查询声音统计信息
	 * @param trackId
	 * @return
	 */
	@Override
	public AlbumStatVo getTrackStatVo(Long trackId) {
		return trackInfoMapper.getTrackStatVo(trackId);
	}

		/**
	 * 获取用户声音分集购买支付列表
	 * @param trackId 选择“付费标识”声音ID
	 * @return [{name:"本集",price:"1",trackCount:1},{name:"后10集",price:"10",trackCount:10},{name:"全集",price:"15",trackCount:15}]
	 */
	@Override
	public List<Map<String, Object>> getUserWaitBuyTrackList(Long trackId) {
		// 1. 获取当前用户id
		Long userId = AuthContextHolder.getUserId();

		// 2. 根据“选择购买声音ID”查询声音信息：得到专辑ID、声音序号(order_num)
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
		Long albumId = trackInfo.getAlbumId();
		// 2.1 根据专辑ID得到专辑列表
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		Integer orderNum = trackInfo.getOrderNum();

		// 3. 根据专辑ID + 声音序号条件查询所有大于等于（ge）指定序号声音列表
		LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(TrackInfo::getAlbumId,albumId);
		queryWrapper.ge(TrackInfo::getOrderNum,orderNum);
		List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);

		// 4. 远程调用“用户服务”得到用户已购买声音ID集合
		List<Long> trackIdList = userFeignClient.getUserPaidTrackList(trackId).getData();

		// 5. 如果存在已购买的声音ID集合，将所有声音列表中已购买声音数据排除掉，得到“未购买声音列表”
		if (CollectionUtil.isNotEmpty(trackIdList)){
			trackInfoList = trackInfoList.stream()
					.filter(trackInfo1 -> !trackIdList.contains(trackInfo1.getId()))
					.collect(Collectors.toList());
		}

		// 6. 根据未购买声音列表长度动态购买声音分集购买列表
		int size = trackInfoList.size();
		// 6.1 构建本集购买对象
		List<Map<String,Object>> list = new ArrayList<>();
		// {name:"本集",price:"1",trackCount:1}
		Map<String,Object> currentMap = new HashMap<>();
		currentMap.put("name","本集");
		currentMap.put("price",albumInfo.getPrice());
		currentMap.put("trackCount",1);
		list.add(currentMap);

		// 6.2 如果未购买的size超过10集，就购买后10集 假设size=25集：后10集  后20集  当25<i,则购买全集
		for (int i =10; i <= 100 ; i += 10) {
			if (size > i){
				Map<String,Object> map = new HashMap<>();
				map.put("name","购买后" + i + "集");
				map.put("price",albumInfo.getPrice().multiply(new BigDecimal(i)));
				map.put("trackCount",i);
				list.add(map);

			}else {
				// 6.3 当购买集数循环不足10集，如25<i,则购买全集
				Map<String,Object> map = new HashMap<>();
				map.put("name","购买全集(共" + size + "集)");
				map.put("price",albumInfo.getPrice().multiply(new BigDecimal(size)));
				map.put("trackCount",size);
				list.add(map);
				break;
			}
		}
		return list;
	}


	/**
	 * 查询当前用户待购买声音列表作为待结算商品列表
	 * @param trackId 选择购买声音ID
	 * @param trackCount 购买集数
	 * @return
	 */
	@Override
	public List<TrackInfo> getPaidTrackInfoList(Long trackId, Integer trackCount) {
		// 1. 根据声音ID查询声音信息得到声音所属专辑ID、序号
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
		Long albumId = trackInfo.getAlbumId();
		Integer orderNum = trackInfo.getOrderNum();

		// 2. 查询声音表中用户未购买过的声音记录
		// 2.1 获取用户已购买的声音ID列表
		List<Long> userPaidTrackList = userFeignClient.getUserPaidTrackList(albumId).getData();
		// 2.2 构建查询声音条件对象
		LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
		if (CollectionUtil.isNotEmpty(userPaidTrackList)){
			queryWrapper.notIn(TrackInfo::getId,userPaidTrackList);
		}
		queryWrapper.eq(TrackInfo::getAlbumId,albumId);
		queryWrapper.ge(TrackInfo::getOrderNum,orderNum);
		queryWrapper.orderByAsc(TrackInfo::getOrderNum);
		queryWrapper.last("limit " + trackCount);
		queryWrapper.select(TrackInfo::getId,TrackInfo::getTrackTitle,TrackInfo::getCoverUrl,TrackInfo::getAlbumId);

		// 2.3 查询待购买的声音列表
		return trackInfoMapper.selectList(queryWrapper);
	}
}
